package com.ansion.commonchat.contact.sync;

import android.Manifest;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.ContactsContract;
import android.text.TextUtils;

import androidx.collection.LongSparseArray;
import androidx.core.content.ContextCompat;


import com.ansion.basedb.dao.LoginedUserMgr;
import com.ansion.basedb.dao.bean.SyncAdapterColumns;
import com.ansion.basedb.dao.helper.UserHelper;
import com.ansion.basedb.dao.model.UserModel;
import com.ansion.commonchat.BaseApplication;
import com.ansion.commonchat.R;
import com.ansion.commonchat.contact.bean.ContactsIds;
import com.ansion.commonchat.contact.bean.SyncUser;
import com.ansion.commonchat.contact.systemcontact.AndroidContactDB;
import com.ansion.commonchat.contact.systemcontact.AndroidContactsFactory;
import com.ansion.commonchat.util.FormatUserIdHelper;
import com.ansion.constant.BConstant;
import com.ansion.log.VLog;
import com.ansion.utils.ContactUtil;
import com.ansion.utils.ResourceUtils;
import com.ansion.utils.SettingHelper;
import com.ansion.utils.SharedPref;
import com.ansion.utils.thread.AbstractTaskThreadLimitCount;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class SyncContactsThread extends AbstractTaskThreadLimitCount {

    private static final String TAG = "SyncContactsThread";

    private static SyncContactsThread mSyncContactsThread = null;

    /**
     * All contacts in device
     */
    private Map<Integer, AndroidContactDB> mAllLocalContacts = new HashMap<Integer, AndroidContactDB>();
//    private Map<String, List<String>> mPhonePrimaryMap = new HashMap<>();

    /**
     * All contacts id in device
     */
    private List<Integer> mAllLocalContactsIds = new ArrayList<Integer>();


    private Set<String> mAllLocalContactsPhones = new HashSet<String>();
    private Set<Long> mAllLocalFormatedContactsPhones = Collections.synchronizedSet(new HashSet<Long>());
    private Set<Long> mBabaUids = new HashSet<Long>();


    private SyncContactsThread() {

        setName(this.getClass().getSimpleName());
    }

    public static synchronized SyncContactsThread getInstance() {

        if (null == mSyncContactsThread) {
            mSyncContactsThread = new SyncContactsThread();
            mSyncContactsThread.setRefreshInterval(10000);
            mSyncContactsThread.setTimeResetLoop(60 * 60 * 1000); // reset count each 60 min
            mSyncContactsThread.setTimeWaitAfterOverlimit(60 * 1000); // sleep 60s if reach loop count (origin 1h)
        }

        return mSyncContactsThread;
    }

//    public void startQueryForUserChange() {
//
//        Set<Long> mBabaUidsNew = UserHelper.loadAllBabaUids(true);
//
//        if (mBabaUidsNew == null) {
//            mBabaUidsNew = new HashSet<Long>();
//        }
//        if (equals(mBabaUids, mBabaUidsNew)) {
//            VLog.i(TAG, "old cocouser may equals new,so no need synccontact");
//            return;
//        } else {
//            mBabaUids.clear();
//            mBabaUids.addAll(mBabaUidsNew);
//            super.startQuery();
//        }
//
//    }

    @Override
    public boolean loadData() {
        if (LoginedUserMgr.getCurrentLoginedUser() == null
                || LoginedUserMgr.getCurrentLoginedUser().getUserId() == ContactUtil.INVALID_ID) {
            return true;
        }

        int countryCode = LoginedUserMgr.getCurrentLoginedUser().getCountryInt();

        if (countryCode < 0) {
            return true;
        }

        VLog.i(TAG, "synccontactBegin");
        long begin = System.currentTimeMillis();

        if (!AndroidContactsFactory.hasAllowReadContacts()) {
            VLog.i(TAG, " not AllowReadContacts.return");
            ContactManager.deleteALLAccount(BaseApplication.getContext());
            return true;
        }

//        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ECLAIR) {
//            return true;
//        }

        try {
            if (ContextCompat.checkSelfPermission(BaseApplication.getContext(), Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
                VLog.i(TAG, "checkSelfPermission false.return");
                return false;
            }
        } catch (Exception e) {
            VLog.e(e, TAG, "checkSelfPermission fail");
        }

        String accountName = getAccountName();

        if (TextUtils.isEmpty(accountName)) {
            VLog.i(TAG, "accountName is empty.return");
            return true;
        }
        int versionCode = 23;
        boolean dealContactUpdate = SettingHelper.getSetting("dealContactUpdate" + versionCode, false);
        if (!dealContactUpdate) {//先把账户删掉，删掉之前创建的消息语音和视频入口

            ContactManager.deleteALLAccount(BaseApplication.getContext());
            SettingHelper.saveSetting("dealContactUpdate" + versionCode, true);
        }
        ContactManager.createAccount(BaseApplication.getContext(), accountName);

        if (!ContactManager.isExistAccount(BaseApplication.getContext(), accountName)) {
            VLog.i(TAG, "create account fail.return");
            return true;
        }

        boolean isInit = init(accountName);
        if (!isInit) {
            return true;
        }

        VLog.i(TAG, "get init cost" + (System.currentTimeMillis() - begin));

        if (!check(mAllLocalContacts, mAllLocalContactsIds)) {
            VLog.i(TAG, "check contact fail.return");
            return true;
        }

        addPhoneBookForApp(accountName);
        return true;
    }


    public static synchronized void exitSyncContacts() {
        if (null != mSyncContactsThread) {
            mSyncContactsThread.destroy();
            mSyncContactsThread = null;
        }
    }

    private String getAccountName() {

        long uid = LoginedUserMgr.getCurrentLoginedUser().getUserId();
        if (ContactUtil.INVALID_ID == uid) {
            return null;
        }
        return uid + "";
    }

    private boolean init(String accountName) {


        VLog.i(TAG, "init start");
        mAllLocalContacts = AndroidContactsFactory.getAllContacts();
//        mPhonePrimaryMap = AndroidContactsFactory.getContactPrimaryPhone();

        mAllLocalContactsPhones = AndroidContactsFactory.getContactPhoneNameMap().keySet();
        mAllLocalFormatedContactsPhones.clear();
        for (String key : mAllLocalContactsPhones) {
            int country = 0;
            UserModel user = LoginedUserMgr.getCurrentLoginedUser();
            if (user != null) {
                country = user.getCountryInt();
            }
            long id = FormatUserIdHelper.getFormatUserid(country, key);
            if (id > 0) {
                mAllLocalFormatedContactsPhones.add(id);
            }
        }

        if (SharedPref.getInstance().getInt(BConstant.Contact.PRE_CONTACT_FIRST_LOAD, -1) == 0) {
            SharedPref.getInstance().putInt(BConstant.Contact.PRE_CONTACT_FIRST_LOAD, 1);
        }
        mAllLocalContactsIds = AndroidContactsFactory.getSortedIds();
        mBabaUids = UserHelper.loadAllBabaUids();
        VLog.i(TAG, "init end");


        return true;
    }

    // 参照telegram实现
    public void addPhoneBookForApp(String accountName) {
        VLog.i(TAG, "addPhoneBookForApp start");
        int countryCode = LoginedUserMgr.getCurrentLoginedUser().getCountryInt();
        final ContentResolver contentResolver = BaseApplication.getContext().getContentResolver();
        Uri rawContactUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName).appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, SyncAdapterColumns.ACCOUNT_TYPE).build();
        Cursor cursor = contentResolver.query(rawContactUri, new String[]{BaseColumns._ID, ContactsContract.RawContacts.SYNC2}, null, null, null);
        LongSparseArray<Long> bookContacts = new LongSparseArray<>();
        if (cursor != null) {
            while (cursor.moveToNext()) {
                long userId = cursor.getLong(1);
                long rawId = cursor.getLong(0);
//                VLog.i(TAG,"load from rawContact userId:%d,rawId:%d",userId,rawId);
                bookContacts.put(userId, rawId);
            }
            cursor.close();
            cursor = null;
        }
        //遍历所有的本地联系人
        for (Integer contactId : mAllLocalContactsIds) {

            AndroidContactDB contact = mAllLocalContacts.get(contactId);

            if (null == contact) {
                continue;
            }
            String contactNameKey = contact.getFirstName() + contact.getLastName();
            //没有联系人名字的不生成
            if (TextUtils.isEmpty(contact.getFirstName()) && TextUtils.isEmpty(contact.getLastName())) {
                continue;
            }


            List<String> phones = contact.getPhonesSort();


            if (null == phones || phones.size() == 0) {
                continue;
            }
            boolean loged = false;
            for (String phone : phones) {

                if (TextUtils.isEmpty(phone)) {
                    continue;
                }
                String originalPhone = contact.getOriginalPhone(phone);
                long formatUserId = FormatUserIdHelper.getFormatUserid(countryCode, originalPhone);
                //从本地联系人中遍历所有已注册用户
                if (formatUserId > 0 && mBabaUids.contains(formatUserId)) {

                    if (!loged) {
                        loged = true;
//                        VLog.i(TAG, "getPhones for:%s,%s", contactNameKey, phones);
                    }

                    SyncUser user = new SyncUser(formatUserId, contact.getName(), originalPhone);
                    user.mContactId = contactId;


                    ContactsIds localContactsIds = ContactManager.lookupLocalContact(contentResolver,
                            accountName, user.mPhone);

                    user.mRawContactId = localContactsIds.mRawContactsId;
                    // local contact not exist
                    if (BConstant.Contact.INVALID_ID == localContactsIds.mContactsId) {
                        continue;
                    }
                    if (bookContacts.indexOfKey(formatUserId) >= 0) {//如果该contact已经创建过了，就不需要再创建应用入口
                        break;
                    }
                    addPhoneBook(accountName, user, contact);
                    VLog.i(TAG, "add for %s,phone:%d", contactNameKey, formatUserId);
                    break;


                }
            }
        }
        VLog.i(TAG, "addPhoneBookForApp end");
    }

    private void addPhoneBook(String accountName, SyncUser user, AndroidContactDB contact) {
        ArrayList<ContentProviderOperation> query = new ArrayList<>();
        ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(addCallerIsSyncAdapterParameter(ContactsContract.RawContacts.CONTENT_URI, true));
        builder.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName);
        builder.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, SyncAdapterColumns.ACCOUNT_TYPE);
        builder.withValue(ContactsContract.RawContacts.SYNC1, TextUtils.isEmpty(user.mPhone) ? "" : user.mPhone);
        builder.withValue(ContactsContract.RawContacts.SYNC2, user.mUserId);
        query.add(builder.build());


        builder = ContentProviderOperation.newInsert(addCallerIsSyncAdapterParameter(ContactsContract.Data.CONTENT_URI, true));
        builder.withValueBackReference(ContactsContract.CommonDataKinds.StructuredName.RAW_CONTACT_ID, 0);
        builder.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
        builder.withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, contact.getFirstName());
        builder.withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, contact.getLastName());
        query.add(builder.build());


        String appLabel = ResourceUtils.getString(R.string.baba_baba);
        builder = ContentProviderOperation.newInsert(addCallerIsSyncAdapterParameter(ContactsContract.Data.CONTENT_URI, true));
        builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0);
        builder.withValue(ContactsContract.Data.DATA1, user.mPhone);
        builder.withValue(ContactsContract.Data.DATA2, appLabel);
        builder.withValue(ContactsContract.Data.DATA3, joinUserId2Label(ResourceUtils.getString(R.string.message), user.mUserId));
        builder.withValue(ContactsContract.Data.DATA4, user.mPhone);
        builder.withValue(ContactsContract.Data.DATA5, user.mUserId);
        builder.withValue(ContactsContract.Data.MIMETYPE, SyncAdapterColumns.MIME_CHAT);
        query.add(builder.build());


        builder = ContentProviderOperation.newInsert(addCallerIsSyncAdapterParameter(ContactsContract.Data.CONTENT_URI, true));
        builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0);
        builder.withValue(ContactsContract.Data.DATA1, user.mPhone);
        builder.withValue(ContactsContract.Data.DATA2, appLabel);
        builder.withValue(ContactsContract.Data.DATA3, joinUserId2Label(ResourceUtils.getString(R.string.send_voicecall_title), user.mUserId));
        builder.withValue(ContactsContract.Data.DATA4, user.mPhone);
        builder.withValue(ContactsContract.Data.DATA5, user.mUserId);
        builder.withValue(ContactsContract.Data.MIMETYPE, SyncAdapterColumns.MIME_CALL_VOICE);
        query.add(builder.build());

        builder = ContentProviderOperation.newInsert(addCallerIsSyncAdapterParameter(ContactsContract.Data.CONTENT_URI, true));
        builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0);
        builder.withValue(ContactsContract.Data.DATA1, user.mPhone);
        builder.withValue(ContactsContract.Data.DATA2, appLabel);
        builder.withValue(ContactsContract.Data.DATA3, joinUserId2Label(ResourceUtils.getString(R.string.send_videocall_title), user.mUserId));
        builder.withValue(ContactsContract.Data.DATA4, user.mPhone);
        builder.withValue(ContactsContract.Data.DATA5, user.mUserId);
        builder.withValue(ContactsContract.Data.MIMETYPE, SyncAdapterColumns.MIME_CALL_VIDEO);
        query.add(builder.build());

        try {
            ContentProviderResult[] result = BaseApplication.getContext().getContentResolver().applyBatch(ContactsContract.AUTHORITY, query);
            if (result != null && result.length > 0 && result[0].uri != null) {
                long res = Long.parseLong(result[0].uri.getLastPathSegment());
                VLog.i(TAG, "applyBatch finish for %d,id:%d,[%s,%s]", user.mUserId, res, contact.getFirstName(), contact.getLastName());
            }
        } catch (Exception ignore) {
            VLog.e(ignore, TAG, "applyBatch fail");
        }


    }

    private static String joinUserId2Label(String label, long userId) {

//		return label;
        return label + " (+" + userId + ")";

    }

    private static Uri addCallerIsSyncAdapterParameter(Uri uri, boolean isSyncOperation) {
        if (isSyncOperation) {
            return uri.buildUpon()
                    .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
                    .build();
        }
        return uri;
    }


//    private static <T> boolean equals(Set<T> oldSet, Set<T> newSet) {
//        if (oldSet == null || oldSet.isEmpty() || newSet == null || newSet.isEmpty()) {
//            return false;
//        }
//        VLog.i(TAG, "oldSet.size,newSet.size--" + oldSet.size() + "," + newSet.size());
//        if (oldSet.size() != newSet.size()) {
//            return false;
//        }
//        for (T s : newSet) {
//            if (!oldSet.contains(s)) {
//                VLog.i(TAG, "oldSet doesnt contains s:==" + s);
//                return false;
//            }
//        }
//        return true;
//    }

    private boolean check(Map<Integer, AndroidContactDB> allLocalContacts, List<Integer> allLocalContactsIds) {
        if (null == allLocalContacts || null == allLocalContactsIds || allLocalContacts.size() == 0
                || allLocalContactsIds.size() == 0) {
            VLog.i(TAG, "loadUIData allContacts is null");
            return false;
        }
        return true;
    }

    private static <T> String joinSetValues(Set<T> a) {
        if (a == null || a.isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (T t : a) {
            sb.append(t.toString());
            sb.append(" ");
        }
        return String.valueOf(sb);
    }

    public synchronized void startQuery() {
        if (VLog.OPEN_LOG)
            VLog.e(TAG, "startQuery");
        if (ContactUtil.contactsPermissionChecked) {
            notifyQuery(false);
        } // else delay
    }


}
